//
// Created by 77873 on 2020/10/10 0010.
//

#include "Decodec.h"



Decodec::Decodec(CallBackToJava *callJava, const char *musicUrl, PlayStatus *ps) {
    this->callBackToJava = callJava;
    this->fileUrl = musicUrl;
    this->playStatus = ps;
    isDecodecFinish = false;
    pthread_mutex_init(&init_mutex, NULL);
    pthread_mutex_init(&seekMutex, NULL);
}

Decodec::~Decodec() {
    pthread_mutex_destroy(&init_mutex);
    pthread_mutex_destroy(&seekMutex);
}


void *decodeFFmpeg(void *data) {
    Decodec *mineFFmpeg = (Decodec *) data;
    mineFFmpeg->decodeFFmpegThread();
//    pthread_exit(&mineFFmpeg->threadDecodec);
    return 0;
}

void Decodec::onParpared() {
    /**
     * 参数1 pthread_t对象
     * 参数2 NULL
     * 参数3 线程回调
     * 参数4 回调的目标
     */
    pthread_create(&threadDecodec, NULL, decodeFFmpeg, this);
}

int avformatCallBack(void *ctx) {
    Decodec *fFmpeg = static_cast<Decodec *>(ctx);
    if (fFmpeg->playStatus->isExit) {
        return AVERROR_EOF;
    }
    return 0;
}

void Decodec::decodeFFmpegThread() {
    LOGD("decodeFFmpegThread start");

    pthread_mutex_lock(&init_mutex);

    avformat_network_init();

    // 封装格式上下文，统领全局的结构体，保存了视频文件封装格式的相关信息
    avFormatContext = avformat_alloc_context();

    //用于检查是否中止阻止功能的回调。
    avFormatContext->interrupt_callback.callback = avformatCallBack;
    avFormatContext->interrupt_callback.opaque = this;

    //step 1  打开视频输入文件
    if (avformat_open_input(&avFormatContext, fileUrl, NULL, NULL) != 0) {
        LOGE("avformat_open_input on error");
        initErrorRelease();
        if (NULL != callBackToJava) {
            callBackToJava->OnCallError(CHILD_THREAD, 1001, "avformat_open_input failed");
        }
        return;
    };

    //step 2  获取视频文件信息
    if (avformat_find_stream_info(avFormatContext, NULL) != 0) {
        LOGE("avformat_find_stream_info on error");
        initErrorRelease();
        if (NULL != callBackToJava) {
            callBackToJava->OnCallError(CHILD_THREAD, 1002, "avformat_find_stream_info failed");
        }
        return;
    }

    //step 3    获取视频流的索引信息，遍历所有类型的流(音频流，视频流，字幕流)，找到视频流


    for (int i = 0; i < avFormatContext->nb_streams; ++i) {
        //视频流
        if (avFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            if (NULL == videoDecodec) {
                //媒体流里面可以拿到时间基
                AVRational baseTime = avFormatContext->streams[i]->time_base;
                //帧率
                AVRational frame_rate = avFormatContext->streams[i]->avg_frame_rate;
                int fps_value = av_q2d(frame_rate);

                videoDecodec = new VideoDecodec(callBackToJava, playStatus);
                videoDecodec->videoStreamsIndex = i;
                videoDecodec->videoBaseTime = baseTime;
                videoDecodec->fps_value = fps_value;
                videoDecodec->videoCodecParameters = avFormatContext->streams[i]->codecpar;
            }
        } else if (avFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {//音频流
            if (NULL == audioDecodec) {
                AVRational baseTime = avFormatContext->streams[i]->time_base;
                int sampleRate = avFormatContext->streams[i]->codecpar->sample_rate;

                audioDecodec = new AudioDecodec(playStatus, callBackToJava, sampleRate);
                audioDecodec->audioStreamsIndex = i;
                audioDecodec->audioBaseTime = baseTime;
                audioDecodec->duration = avFormatContext->duration / AV_TIME_BASE;
                videoDuration = audioDecodec->duration;

                audioDecodec->audioCodecParameters = avFormatContext->streams[i]->codecpar;
            }
        }
    }

    if (NULL != videoDecodec) {
        if (initAvCodecContext(videoDecodec->videoCodecParameters,
                               &videoDecodec->videoCodecContex) < 0) {
            isDecodecFinish = true;
            pthread_mutex_unlock(&init_mutex);
        };
    }
    if (NULL != audioDecodec) {
        if (initAvCodecContext(audioDecodec->audioCodecParameters,
                               &audioDecodec->audioCodecContex) < 0) {
            isDecodecFinish = true;
            pthread_mutex_unlock(&init_mutex);
        };
    }

    LOGD("decodeFFmpegThread end  start callbackTojava");

    //视频中没有音频也没有视频
    if (!audioDecodec && !videoDecodec) {
        LOGD("audioDecodec && videoDecodec  is null");

        initErrorRelease();
        if (NULL != callBackToJava) {
            callBackToJava->OnCallError(CHILD_THREAD, 1003, "audio && video is null");
        }
        return;
    }
    if (NULL != callBackToJava) {
        LOGD("OnCallParpared");
        if (NULL != playStatus && !playStatus->isExit) {
            callBackToJava->OnCallParpared(CHILD_THREAD);
        } else {
            isDecodecFinish = true;
        }
    }
    pthread_mutex_unlock(&init_mutex);
}

void Decodec::initErrorRelease() {
    isDecodecFinish = true;
    pthread_mutex_unlock(&init_mutex);
    avformat_free_context(avFormatContext);
    av_free(avFormatContext);
    avFormatContext = NULL;
}

void Decodec::start() {
    if (NULL == audioDecodec) {
        LOGE("audio  is null");
        return;
    }
    if (NULL == videoDecodec) {
        LOGE("video is null");
        return;
    }
    isSupportMediaCodec = false;

    videoDecodec->audioDecodec = audioDecodec;

    /**
     * 硬解码
     */
    const char *codecName = ((const AVCodec *) videoDecodec->videoCodecContex->codec)->name;
    if (isSupportMediaCodec = callBackToJava->onCllIsSupportMediaCodec(codecName)) {
        LOGE("当前设备支持硬解码此视频");

        if (strcasecmp(codecName, "h264") == 0) {
            avBitStreamFilter = av_bsf_get_by_name("h264_mp4toannexb");
        } else if (strcasecmp(codecName, "h265") == 0) {
            avBitStreamFilter = av_bsf_get_by_name("hevc_mp4toannexb");
        }
        if (NULL == avBitStreamFilter) {
            goto end;
        }
        if (av_bsf_alloc(avBitStreamFilter, &videoDecodec->avbsfContext) != 0) {
            isSupportMediaCodec = false;
            goto end;
        }
        if (avcodec_parameters_copy(videoDecodec->avbsfContext->par_in,
                                    videoDecodec->videoCodecParameters) < 0) {
            isSupportMediaCodec = false;
            av_bsf_free(&videoDecodec->avbsfContext);
            videoDecodec->avbsfContext = NULL;
            goto end;
        }
        if (av_bsf_init(videoDecodec->avbsfContext) != 0) {
            isSupportMediaCodec = false;
            av_bsf_free(&videoDecodec->avbsfContext);
            videoDecodec->avbsfContext = NULL;
            goto end;
        }
        videoDecodec->avbsfContext->time_base_in = videoDecodec->videoBaseTime;


    }

    end:
//    isSupportMediaCodec= false;
    if (isSupportMediaCodec) {
        videoDecodec->codecType = CODEC_MEDIACODEC;

        callBackToJava->onCallInitMediaCodec(
                codecName,
                videoDecodec->videoCodecContex->width,
                videoDecodec->videoCodecContex->height,
                videoDecodec->videoCodecContex->extradata_size,
                videoDecodec->videoCodecContex->extradata_size,
                videoDecodec->videoCodecContex->extradata,
                videoDecodec->videoCodecContex->extradata
                );

    }

    audioDecodec->startPlay();
    videoDecodec->startPlay();


    while (NULL != playStatus && !playStatus->isExit) {
        if (playStatus->isSeek) {
            av_usleep(1000 * 100);
            continue;
        }

        if (audioDecodec->audioDataQueue->getQueueSIze() > 100) {
            av_usleep(1000 * 100);
            continue;
        }

        AVPacket *avPacket = av_packet_alloc();
        pthread_mutex_lock(&seekMutex);
        int ret = av_read_frame(avFormatContext, avPacket);
        pthread_mutex_unlock(&seekMutex);

        if (ret == 0) {
            if (avPacket->stream_index == audioDecodec->audioStreamsIndex) {
                //如果是音频流
                audioDecodec->audioDataQueue->pushAvpacket(avPacket);
            } else if (avPacket->stream_index == videoDecodec->videoStreamsIndex) {
                //如果是视频流
                videoDecodec->videoDataQueue->pushAvpacket(avPacket);
            } else {
                av_packet_free(&avPacket);
                av_free(avPacket);
            }


        } else {
            if (LOG_DEBUG) {
                LOGE("decode  finished ");
            }


            av_packet_free(&avPacket);
            av_free(avPacket);
//            avPacket = NULL;

            while (NULL != playStatus && !playStatus->isExit) {
                if (audioDecodec->audioDataQueue->getQueueSIze() > 0) {
//                    LOGE("解码完成后，audioDecodec 队列数量大于0");
                    av_usleep(1000 * 100);
                    continue;
                } else {
//                    LOGE("解码完成后，audioDecodec 队列数量小于0");
                    if (!playStatus->isSeek) {
//                        LOGE("解码完成后，audioDecodec 队列数量小于0  并且不再seek中");
                        av_usleep(1000 * 100);
                        playStatus->isExit = true;
                    }
                    break;
                }
            }
//            break;
        }
    }
    if (NULL != callBackToJava) {
        callBackToJava->OnCallComplete(CHILD_THREAD);
    }
    isDecodecFinish = true;

    if (LOG_DEBUG) {
        LOGE("解码已完成");
    }
}

void Decodec::pause() {
    if (NULL != playStatus) {
        playStatus->pause = true;
    }

    if (NULL != audioDecodec) {
        audioDecodec->pause();
    }
}

void Decodec::resume() {
    if (NULL != playStatus) {
        playStatus->pause = false;
    }
    if (NULL != audioDecodec) {
        audioDecodec->resume();
    }
}

void Decodec::release() {
    playStatus->isExit = true;

    pthread_join(threadDecodec,NULL);


    pthread_mutex_lock(&init_mutex);

    int sleepCount = 0;
    //解码未完成
    while (!isDecodecFinish) {
        if (sleepCount > 1000) {
            isDecodecFinish = true;
        }
        LOGE("wait decodec exit %d ", sleepCount);
        sleepCount++;
        av_usleep(1000 * 10);
    }

//    释放资源

    if (NULL != audioDecodec) {
        audioDecodec->release();
        delete (audioDecodec);
        audioDecodec = NULL;
    }

    if (NULL != videoDecodec) {
        videoDecodec->release();
        delete (videoDecodec);
        videoDecodec = NULL;
    }


    if (NULL != avFormatContext) {
        avformat_close_input(&avFormatContext);
        avformat_free_context(avFormatContext);
        avFormatContext = NULL;
    }

    if (NULL != callBackToJava) {
        callBackToJava = NULL;
    }


    if (NULL != playStatus) {
        playStatus = NULL;
    }

    pthread_mutex_unlock(&init_mutex);
}

int Decodec::initAvCodecContext(AVCodecParameters *codecParameters,
                                AVCodecContext **currentCodecContext) {



    /**
     * 根据 编码类型获取到对应的编码器
     */
    AVCodec *dec = avcodec_find_decoder(codecParameters->codec_id);

    if (!dec) {
        if (LOG_DEBUG) {
            LOGE("avcodec_find_decoder error");
            callBackToJava->OnCallError(CHILD_THREAD, 1003, "avcodec_find_decoder error");
        }
        return -1;
    }

    *currentCodecContext = avcodec_alloc_context3(dec);

    if (!currentCodecContext) {
        if (LOG_DEBUG) {
            LOGE("avcodec_alloc_context3   error");
            callBackToJava->OnCallError(CHILD_THREAD, 1004, "avcodec_alloc_context3 error");
        }
        return -1;
    }
    /**
   *根据提供的编解码器中的值填充编解码器上下文参数。
   */
    if (avcodec_parameters_to_context(*currentCodecContext,
                                      codecParameters) < 0) {

        if (LOG_DEBUG) {
            LOGE("avcodec_parameters_to_context error");
            callBackToJava->OnCallError(CHILD_THREAD, 1005, "avcodec_parameters_to_context error");
        }
        return -1;
    }


    if (avcodec_open2(*currentCodecContext, dec, 0) != 0) {
        if (LOG_DEBUG) {
            LOGE("cant not openg strames");
        }
        callBackToJava->OnCallError(CHILD_THREAD, 1006, "cant not open stream");
        return -1;
    }
    return 0;
}

void Decodec::seek(int64_t second) {
    if (videoDuration <= 0) {
        return;
    }
    if (second >= 0 && second <= videoDuration) {
        playStatus->isSeek = true;
        pthread_mutex_lock(&seekMutex);
        int64_t rel = second * AV_TIME_BASE;
        int result = avformat_seek_file(avFormatContext, -1, INT64_MIN, rel, INT64_MAX, 0);
        if (result >= 0) {
            if (NULL != audioDecodec) {
                audioDecodec->audioDataQueue->clearAvpacket();
                audioDecodec->clock = 0;
                audioDecodec->last_time = 0;
                pthread_mutex_lock(&audioDecodec->codecMutex);
                avcodec_flush_buffers(audioDecodec->audioCodecContex);
                pthread_mutex_unlock(&audioDecodec->codecMutex);
            }
            if (NULL != videoDecodec) {
                videoDecodec->videoDataQueue->clearAvpacket();
                pthread_mutex_lock(&videoDecodec->codecMutex);
                avcodec_flush_buffers(videoDecodec->videoCodecContex);
                pthread_mutex_unlock(&videoDecodec->codecMutex);
            }
        } else {
            LOGE("avformat_seek_file  failed");
        }
        pthread_mutex_unlock(&seekMutex);
        playStatus->isSeek = false;
    }
}

void Decodec::setVolume(int percnet) {
    if (NULL != audioDecodec) {
        audioDecodec->setVolume(percnet);
    }
}

void Decodec::setMute(int mute) {
    if (NULL != audioDecodec) {
        audioDecodec->setMute(mute);
    }
}







